package com.soccertgc.core.game
{
	
	import com.soccertgc.core.DispatcherDataObject;
	import com.soccertgc.core.Position;
	
	import flash.events.Event;
	import flash.utils.Dictionary;

	public class GameFormation extends DispatcherDataObject
	{
	
		// -------------------------------------------------------------------------------
		// CONSTANTS
		// -------------------------------------------------------------------------------

        // JSON properties names
		protected static const		JSON_PROPERTY_GK:String                        	= "gk";
		protected static const		JSON_PROPERTY_D1:String                        	= "d1";
		protected static const		JSON_PROPERTY_D2:String                        	= "d2";
		protected static const		JSON_PROPERTY_MF:String                        	= "mf";
		protected static const		JSON_PROPERTY_F1:String                        	= "f1";
		protected static const		JSON_PROPERTY_F2:String                        	= "f2";

		// Events
		public static const         EVENT_FORMATION_CHANGED:String					= "game_formation_changed";
		public static const         EVENT_FORMATION_PLAYER_SWAP:String				= "game_formation_player_swap";
		public static const         EVENT_FORMATION_PLAYER_CHANGED:String			= "game_formation_player_changed";
		
		// -------------------------------------------------------------------------------
		// FIELDS
		// -------------------------------------------------------------------------------
		
		private var _formation:Dictionary;
		private var	_gameTeam:GameTeam;
		
		// -------------------------------------------------------------------------------
		// CONSTRUCTOR
		// -------------------------------------------------------------------------------
		
		public function GameFormation(gameTeam:GameTeam)
		{
			// Ancestor constructor
			super();
			// Game reference
			_gameTeam = gameTeam;
			// Init
			_formation = new Dictionary(true);
			_formation[Position.GOALKEEPER] 	= null;
			_formation[Position.DEFENDER_01]	= null;
			_formation[Position.DEFENDER_02]	= null;
			_formation[Position.MIDFIELD]   	= null;
			_formation[Position.FORWARD_01] 	= null;
			_formation[Position.FORWARD_02] 	= null;
		}
		
		// -------------------------------------------------------------------------------
		// PROPERTIES
		// -------------------------------------------------------------------------------
		
		
		[Bindable(event="game_formation_player_changed")]
		public function get goalkeeper():GamePlayerCard
		{
			return getPlayer(Position.GOALKEEPER);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get hasGoalkeeper():Boolean
		{
			return hasPlayer(Position.GOALKEEPER);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get defender01():GamePlayerCard
		{
			return getPlayer(Position.DEFENDER_01);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get hasDefender01():Boolean
		{
			return hasPlayer(Position.DEFENDER_01);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get defender02():GamePlayerCard
		{
			return getPlayer(Position.DEFENDER_02);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get hasDefender02():Boolean
		{
			return hasPlayer(Position.DEFENDER_02);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get midfield():GamePlayerCard
		{
			return getPlayer(Position.MIDFIELD);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get hasMidfield():Boolean
		{
			return hasPlayer(Position.MIDFIELD);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get forward01():GamePlayerCard
		{
			return getPlayer(Position.FORWARD_01);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get hasForward01():Boolean
		{
			return hasPlayer(Position.FORWARD_01);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get forward02():GamePlayerCard
		{
			return getPlayer(Position.FORWARD_02);
		}
		
		[Bindable(event="game_formation_player_changed")]
		public function get hasForward02():Boolean
		{
			return hasPlayer(Position.FORWARD_02);
		}
		
		public function get isValid():Boolean
		{
			return	hasGoalkeeper	&&
					hasDefender01	&&
					hasDefender02	&&
					hasMidfield		&&
					hasForward01	&&
					hasForward02;
		}
		
		// -------------------------------------------------------------------------------
		// PRIVATE METHODS
		// -------------------------------------------------------------------------------
		
		protected function setPlayer(position:Position, playerCard:GamePlayerCard):void
		{
			var player:GamePlayerCard = getPlayer(position);
			// Apply the change only if the player is changed
			if (player == null || !player.equals(playerCard))
			{
				// Sets the player reference into the right position
				_formation[position] = playerCard;
				// Apply the player position
				playerCard.updatePosition(position);
				// Dispatch EVENT_PLAYER_CHANGED
				dispatchFormationPlayerChanged(playerCard);
			}
		}
		
		protected function updatePlayer(position:Position, playerCard:GamePlayerCard):void
		{
			var changed:Boolean = false;
			var player:GamePlayerCard = getPlayer(position);
			if (player != null)
			{
				// Apply the change only if the player is changed
				if (!player.equals(playerCard))
				{
					// Updates the card with the current values
					player.copy(playerCard);
					// Changed
					changed = true;
				}
			}
			else
			{
				// Sets the player reference into the right position
				_formation[position] = playerCard;
				// Apply the player position
				playerCard.updatePosition(position);
				// Changed
				changed = true;
			}
			// If the player has been updated, dispatch EVENT_PLAYER_CHANGED event 
			if (changed) dispatchFormationPlayerChanged(playerCard);
		}
		
		// DataObject implementation
		
		override protected function validate(data:Object):void
		{
			if (!data.hasOwnProperty(JSON_PROPERTY_GK)) throwLoadMissingPropError(JSON_PROPERTY_GK);
			if (!data.hasOwnProperty(JSON_PROPERTY_D1)) throwLoadMissingPropError(JSON_PROPERTY_D1);
			if (!data.hasOwnProperty(JSON_PROPERTY_D2)) throwLoadMissingPropError(JSON_PROPERTY_D2);
			if (!data.hasOwnProperty(JSON_PROPERTY_MF)) throwLoadMissingPropError(JSON_PROPERTY_MF);
			if (!data.hasOwnProperty(JSON_PROPERTY_F1)) throwLoadMissingPropError(JSON_PROPERTY_F1);
			if (!data.hasOwnProperty(JSON_PROPERTY_F2)) throwLoadMissingPropError(JSON_PROPERTY_F2);
		}
		
		override protected function fill(data:Object):void
		{
			// Retrieve players raw data
			var raw_gk:Object = data[JSON_PROPERTY_GK];
			var raw_d1:Object = data[JSON_PROPERTY_D1];
			var raw_d2:Object = data[JSON_PROPERTY_D2];
			var raw_mf:Object = data[JSON_PROPERTY_MF];
			var raw_f1:Object = data[JSON_PROPERTY_F1];
			var raw_f2:Object = data[JSON_PROPERTY_F2];
			// Creates the GamePlayerCards
			var gk:GamePlayerCard = new GamePlayerCard(raw_gk);
			var d1:GamePlayerCard = new GamePlayerCard(raw_d1);
			var d2:GamePlayerCard = new GamePlayerCard(raw_d2);
			var mf:GamePlayerCard = new GamePlayerCard(raw_mf);
			var f1:GamePlayerCard = new GamePlayerCard(raw_f1);
			var f2:GamePlayerCard = new GamePlayerCard(raw_f2);			
			// Updates the players
			updatePlayer(Position.GOALKEEPER, 	gk);
			updatePlayer(Position.DEFENDER_01, 	d1);
			updatePlayer(Position.DEFENDER_02, 	d2);
			updatePlayer(Position.MIDFIELD, 	mf);
			updatePlayer(Position.FORWARD_01, 	f1);
			updatePlayer(Position.FORWARD_02, 	f2);
		}
		
		// Event dispatchers

		protected function dispatchFormationPlayerSwap(positionSource:Position, playerSource:GamePlayerCard, positionDestination:Position, playerDestination:GamePlayerCard):void
        {
			// LT_TODO: create GamePlaryerSwapEvent event
			dispatchEvent(new Event(EVENT_FORMATION_PLAYER_SWAP));
        }
		
		protected function dispatchFormationPlayerChanged(player:GamePlayerCard):void
		{
			// LT_TODO: create GamePlayerChangeEvent event
			dispatchEvent(new Event(EVENT_FORMATION_PLAYER_SWAP));
		}
		
		protected function dispatchFormationChanged():void
        {
			dispatchEvent(new Event(EVENT_FORMATION_CHANGED));
        }
		
		// -------------------------------------------------------------------------------
		// PUBLIC METHODS
		// -------------------------------------------------------------------------------
	
		public function hasPlayer(position:Position):Boolean
		{
			return _formation[position] != null;
		}
		
		public function getPlayer(position:Position):GamePlayerCard
		{
			return _formation[position] as GamePlayerCard;
		}
		
		public function getPlayerPosition(player:GamePlayerCard):Position
		{
			for (var pos:Object in _formation)
			{
				var p:GamePlayerCard = _formation[pos] as GamePlayerCard;
				if (p.instanceId == player.instanceId) return pos as Position;
			}
			return null;
		}
		
		public function getPlayerById(instanceId:uint):GamePlayerCard
		{
			for each (var player:GamePlayerCard in _formation)
			{
				if (player.instanceId == instanceId)
				{
					return player;
				}
			}
			return null;
		}
		
		public function swapPlayers(source:Position, destination:Position):void
		{
			// Goalkeeper check
			if (source == Position.GOALKEEPER) 		throw new Error("GOALKEEPER can't be a swap source");
			if (destination == Position.GOALKEEPER) throw new Error("GOALKEEPER can't be a swap destination");
			// Retrieve players to swap
			var sourcePlayer:GamePlayerCard = getPlayer(source);
			var destinationPlayer:GamePlayerCard = getPlayer(source);
			// Swap players
			_formation[source] = destinationPlayer;
			_formation[destination] = sourcePlayer;
			// Update player positions
			destinationPlayer.updatePosition(source);
			sourcePlayer.updatePosition(destination);
			// Dispatch EVENT_PLAYER_SWAP event
			dispatchFormationPlayerSwap(source, sourcePlayer, destination, destinationPlayer);
		}
		
		public function equals(gameTeamFormation:GameFormation):Boolean
		{
			return  ( (!hasGoalkeeper	&& !gameTeamFormation.hasGoalkeeper)    || getPlayer(Position.GOALKEEPER).equals(gameTeamFormation.goalkeeper)  ) &&
                    ( (!hasDefender01   && !gameTeamFormation.hasDefender01)    || getPlayer(Position.DEFENDER_01).equals(gameTeamFormation.defender01) ) &&
                    ( (!hasDefender02   && !gameTeamFormation.hasDefender02)    || getPlayer(Position.DEFENDER_02).equals(gameTeamFormation.defender02) ) &&
                    ( (!hasMidfield     && !gameTeamFormation.hasMidfield)      || getPlayer(Position.MIDFIELD).equals(gameTeamFormation.midfield)      ) &&
                    ( (!hasForward01    && !gameTeamFormation.hasForward01)     || getPlayer(Position.FORWARD_01).equals(gameTeamFormation.forward01)   ) &&
                    ( (!hasForward02    && !gameTeamFormation.hasForward02)		|| getPlayer(Position.FORWARD_02).equals(gameTeamFormation.forward02)	);
		}
		
		public function update(formation:GameFormation):void
		{
			// Clear all game player cards
			_formation[Position.GOALKEEPER] 	= _gameTeam.game.deck.findPlayerCard(formation.goalkeeper.instanceId);
			_formation[Position.DEFENDER_01]	= _gameTeam.game.deck.findPlayerCard(formation.defender01.instanceId);
			_formation[Position.DEFENDER_02]	= _gameTeam.game.deck.findPlayerCard(formation.defender02.instanceId);
			_formation[Position.MIDFIELD]   	= _gameTeam.game.deck.findPlayerCard(formation.midfield.instanceId);
			_formation[Position.FORWARD_01] 	= _gameTeam.game.deck.findPlayerCard(formation.forward01.instanceId);
			_formation[Position.FORWARD_02] 	= _gameTeam.game.deck.findPlayerCard(formation.forward02.instanceId);
			// Dispatch EVENT_FORMATION_CHANGED
			dispatchFormationChanged();
		}
		
		override public function clear():void
		{
			// Clear all game player cards
			_formation[Position.GOALKEEPER] 	= null;
			_formation[Position.DEFENDER_01]	= null;
			_formation[Position.DEFENDER_02]	= null;
			_formation[Position.MIDFIELD]   	= null;
			_formation[Position.FORWARD_01] 	= null;
			_formation[Position.FORWARD_02] 	= null;
			// Dispatch EVENT_FORMATION_CHANGED
			dispatchFormationChanged();
		}
	
	}
	
}